package structures;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

public class Scheduler {
	private Queue<LockNode> processQueue;
	private List<Transaction> transactions;
	private List<LockNode> lockTable;
	private Queue<LockNode> waitingQueue;
	private int [] [] grafo = new int [0] [0];
	
	public Scheduler() {
		processQueue = new LinkedList<LockNode>();
		transactions = new ArrayList<Transaction>();
		lockTable = new ArrayList<LockNode>();
		waitingQueue = new LinkedList<LockNode>();
	}

	public void receiveOperation(Operation newOperation) {
		if (newOperation.getObject().equals("Commit")) {
			// TO-DO
		} else if (acceptLock(newOperation)) {
			identifyTransaction(newOperation);
			MultipleGranularity(newOperation);
			addGrantedLock(newOperation.getType(), newOperation.getObject(), newOperation.getTransactionId());
			LockNode ln = new LockNode(newOperation.getType(), newOperation.getObject(), newOperation.getTransactionId());
			processQueue.add(ln);
		} else
			addWaitingLock(newOperation.getType(), newOperation.getObject(),
					newOperation.getTransactionId());
	}

	private void identifyTransaction(Operation newOperation) {
		boolean existTransaction = false;
		for (Transaction transaction : transactions) {
			if (transaction.getTransactionId() == newOperation.getTransactionId()) {
				transaction.getOperations().add(newOperation);
				existTransaction = true;
			}
		}
		if (!existTransaction) {
			Transaction transaction = new Transaction(transactions.size() + 1);
			transaction.setPos(transactions.size());
			transaction.addOperation(newOperation);
			transactions.add(transaction);
			grafo = incluirTranasacaoNoGrafo(grafo);
		}
	}

	// public void processingOperation() {
	// LockNode processing = processQueue.peek();
	// if (existLockOnObject(processing.getObject())) {
	// waitingQueue.add(processing);
	// } else {
	// addGrantedLock(processing.getType(), processing.getObject(),
	// processing.getTransactionId());
	// MultipleGranularity(processing);
	// processedOperation();
	// }
	// }

	private void addGrantedLock(Lock type, Object object, int transactionId) {
		LockNode newLock = new LockNode(type, object, transactionId);
		newLock.setState(1);
		lockTable.add(newLock);
	}

	private void addWaitingLock(Lock type, Object object, int transactionId) {
		LockNode newLock = new LockNode(type, object, transactionId);
		newLock.setState(2);
		waitingQueue.add(newLock);
		printWaitingLockTable();
	}


	public boolean existLockOnObject(Object object) {
		for (LockNode lock : lockTable) {
			if (lock.getObject().equals(object)) {
				return true;
			}
		}
		return false;
	}

	private void MultipleGranularity(Operation processing) {
		Object object = processing.getObject();
		Lock lock = processing.getType();
		if (processing.getObject() instanceof Row) {
			Row row = (Row) object;
			Page page = row.getParent();
			addGrantedLock((lock == Lock.RL ? Lock.IRL : Lock.IWL), page,
					processing.getTransactionId());
			Table table = page.getParent();
			addGrantedLock((lock == Lock.RL ? Lock.IRL : Lock.IWL), table,
					processing.getTransactionId());
		} else if (processing.getObject() instanceof Page) {
			Page page = (Page) object;
			Table table = page.getParent();
			addGrantedLock((lock == Lock.RL ? Lock.IRL : Lock.IWL), table,
					processing.getTransactionId());
		}
		blockSons(processing);
	}

	public void printLockTable() {
		System.out.println("++++++++++++++++++ LOCK TABLE++++++++++++++++++++++++++");
		System.out.println("-TID---------Object-------------Lock------Permission---");
		for (LockNode ln : lockTable) {
			System.out.print("| " + ln.getTransactionId() + " | ");
			System.out.print(ln.getObject().toString() + "  | ");
			System.out.print(ln.getLock() + "   |  ");
			System.out.print(ln.getState() + " |    ");
			System.out.println();
			System.out.println("---------------------------------------------------");
		}
		System.out.println();
	}
	
	
	public void printWaitingLockTable() {
		System.out.println("+++++++++++++++++ WAITING LOCK TABLE +++++++++++++++++++");
		System.out.println("-TID---------Object-------------Lock------Permission---");
		for (LockNode ln : waitingQueue) {
			System.out.print("| " + ln.getTransactionId() + " | ");
			System.out.print(ln.getObject().toString() + "  | ");
			System.out.print(ln.getLock() + "   |  ");
			System.out.print(ln.getState() + " |    ");
			System.out.println();
			System.out.println("---------------------------------------------------");
		}
		System.out.println();
	}

	private void blockSons(Operation op) {
		if (op.getObject() instanceof Table) {
			Table tab = (Table) op.getObject();
			for (Page p : tab.getPages()) {
				for (Row tup : p.getRows()) {
					tup.setBlock(op.getType());
					addGrantedLock(tup.getBlock(), tup, op.getTransactionId());
				}
				p.setBlock(op.getType());
				addGrantedLock(p.getBlock(), p, op.getTransactionId());
			}
		} else if (op.getObject() instanceof Page) {
			Page pag = (Page) op.getObject();
			for (Row tup : pag.getRows()) {
				tup.setBlock(op.getType());
				addGrantedLock(tup.getBlock(), tup, op.getTransactionId());
			}
		}
	}

	private boolean acceptLock(Operation op) {
		// consultar tabela do slide pra ver as compatibilidades
		if (lockTable.size() == 0) {
			return true;
		}
		if (existLockOnObject(op.getObject())){
			for (LockNode x : lockTable) {
				if (x.getObject().equals(op.getObject())) {
					if (x.getLock().equals(Lock.RL) && op.getType().equals(Lock.RL)){
						return true;
					}
				}
			}
		}else{
			return true;
		}
		return false;
	}
	
	private int [] [] incluirTranasacaoNoGrafo(int [] [] grafo){
		int [] [] matriz = new int [grafo.length+1] [grafo.length+1];
		for(int i = 0; i < grafo.length; i++){
			for(int j = 0; j < grafo.length; j++){
				matriz[i][j] = grafo[i][j];
			}
		}
		return matriz;
	}
}
